package org.gluedom.transport;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import com.google.common.collect.ImmutableMap;

public final class Connection implements Comparable<Connection> {
	private final Map<String, String> properties;
	private final Class<? extends TransportFactory> factoryClass;

	private volatile int hash = 0;

	private Connection(ConnectionBuilder builder) {
		this.properties = ImmutableMap.copyOf(builder.properties);
		this.factoryClass = builder.factoryClass;
	}

	public String getProperty(String name) {
		return properties.get(name);
	}

	Class<? extends TransportFactory> getFactoryClass() {
		return factoryClass;
	}

	public static abstract class ConnectionBuilder {
		protected final Map<String, String> properties = new HashMap<String, String>();
		protected Class<? extends TransportFactory> factoryClass;

		public Connection build() {
			return new Connection(this);
		}
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (!(obj instanceof Connection))
			return false;
		Connection other = (Connection) obj;
		if (hashCode() != other.hashCode())
			return false;
		if (!properties.equals(other.properties))
			return false;
		return true;
	}

	@Override
	public int hashCode() {
		if (hash == 0) {
			int result = 150185;
			result = 31 * properties.hashCode();
			if (result == 0)
				result = 1;
			hash = result;
		}
		return hash;
	}

	@Override
	public String toString() {
		return "ConnectionDetails: " + properties.toString();
	}

	@Override
	public int compareTo(Connection other) {
		if (other == null)
			return 1;
		if (equals(other))
			return 0;
		if (hashCode() < other.hashCode())
			return -1;
		if (hashCode() == other.hashCode()) {
			if (properties.size() != other.properties.size())
				return properties.size() - other.properties.size();
			Set<String> pset = new TreeSet<String>(properties.keySet());
			Set<String> oset = new TreeSet<String>(other.properties.keySet());
			StringBuilder pb = new StringBuilder();
			for (String s : pset)
				pb.append(s);
			StringBuilder ob = new StringBuilder();
			for (String s : oset)
				ob.append(s);
			int i = pb.toString().compareTo(ob.toString());
			if (i != 0)
				return i;
			for (String s : pset) {
				i = properties.get(s).compareTo(other.properties.get(s));
				if (i != 0)
					return i;
			}
			// this can never happen! (the maps are equal, but equals returned
			// false)
			throw new AssertionError();
		}
		return 1;
	}
}
